package com.neusoft.util;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * HTTP客户端发送请求类
 * @author 郑兴龙 zheng-xl@neusoft.com
 *
 */
public class HttpClientSendRequest {
	/**
	 * 日志
	 */
	public static Logger logger = Logger.getLogger("" + HttpClientSendRequest.class + "");
	 /**
     * 解析响应消息得到的各字段（鉴权用到）
     */
    private static AuthenticateDigest digestTmp = new AuthenticateDigest();
    static
    {
        digestTmp.setCnonce("");
        digestTmp.setNc("");
        digestTmp.setNonce("");
        digestTmp.setOpaque("");
        digestTmp.setPassword(AuthorizationConstants.AUTHORIZATION_KEY_VALUE);
        digestTmp.setQop(AuthorizationConstants.AUTHORIZATION_QOP_VALUE);
        digestTmp.setRealm(AuthorizationConstants.AUTHORIZATION_REALM_VALUE);
        digestTmp.setResponse("");
        digestTmp.setUri("");
        digestTmp.setUserName(AuthorizationConstants.AUTHORIZATION_USER_VALUE);
    }
	
    /**
     * 消息头CONNECTION_KEY
     */
    private static String connection = "close";
    private static String HTTPS = "https";
    private static String keyStorePassword = "rpkT0orv";
    /**
     * 时区
     */
    private static String timeZoon = " GMT";
    /**
     * 日期转换格式
     */
    private static String dateFormat = "EEE,dd MMM yyyy HH:mm:ss";
    /**
     * 冒号，ip、port拼接符号
     */
    private static String urlConnectChar = ":";
    /**
     * 文件名分割符 
     **/
    public static final String FILE_NAME_SPILT = "_"; 
   
    /**
     * 文件格式
     */
    private static String fileType = "text/xml";
    /**
     * 编码格式
     */
    private static String unicodeType = "utf-8";
    
    /**
     *失败返回 401
     */
    private static int  result401 =  401;
    /**
     * buffer初始长度
     */
    private static int bufferLength = 300;
    /**
     * 证书路径
     */
	private static String keystoreFile = "eIVS.keystore";
	
	private static String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
	 
	 /**
     * 执行http发送下载请求
     * @param xmlData 请求消息
     * @param nvsUrl 访问url
     * @param client 客户端
     * @param method method
     * @throws PortalException PortalException
     */
    public static void getMethodResult(String xmlData, String nvsUrl, HttpClient client, PostMethod method)
       
    {
        int httpTimeOut = 0;
        
        // 设置http连接超时
        client.getHttpConnectionManager().getParams().setConnectionTimeout(httpTimeOut);
        method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, httpTimeOut);
        
        //添加通用头域
        method.addRequestHeader(AuthorizationConstants.REQUEST_HEADER_REQ_URI_KEY, nvsUrl);
        method.addRequestHeader(AuthorizationConstants.REQUEST_HEADER_HTTP_VERSION_KEY,
            AuthorizationConstants.REQUEST_HEADER_HTTP_VERSION_VALUE);
        method.addRequestHeader(AuthorizationConstants.RESPONSE_HEADER_CONNECTION_KEY, connection);
        
        // 设置http请求消息头 DATE
        Date currentDate = new Date();
        String responseHeaderDateValue = new SimpleDateFormat(dateFormat, Locale.US).format(currentDate) + timeZoon;
        method.addRequestHeader(AuthorizationConstants.RESPONSE_HEADER_DATE_KEY, responseHeaderDateValue);
        
        // 设置http请求消息头 Host
        method.addRequestHeader(AuthorizationConstants.REQUEST_HEADER_HOST_KEY, client.getHostConfiguration().getHost()
            + urlConnectChar + client.getHostConfiguration().getPort());
        
        // 设置http请求消息头 User-Agent
        method.addRequestHeader(AuthorizationConstants.REQUEST_HEADER_USER_AGENT_KEY,
            AuthorizationConstants.REQUEST_HEADER_USER_AGENT_VALUE);
        
        // 设置http请求消息头Content-Length
        int length = 0;
        try {
            length = xmlData == null ? 0 : xmlData.getBytes("UTF-8").length;
        }
        catch (UnsupportedEncodingException e1) {
        	e1.getStackTrace();
        }
        method.addRequestHeader(AuthorizationConstants.RESPONSE_HEADER_CONTENT_LENGTH_KEY, String.valueOf(length));
        
        // 获取加密后用于鉴权的值
        digestTmp.setResponse(SHA256DigestUtil.generateResponse(digestTmp, null));
        
        //各字段拼接生成请求的头类似Digest realm="huawei.com",qop="auth"
        StringBuffer authorization = getAuthorization();
        method.addRequestHeader(AuthorizationConstants.RESPONSE_HEADER_AUTHORIZATION_KEY, authorization.toString());
        
        method.addRequestHeader(AuthorizationConstants.REQUEST_HEADER_ACCEPT_KEY,
            AuthorizationConstants.REQUEST_HEADER_ACCEPT_VALUE);
        method.addRequestHeader(AuthorizationConstants.RESPONSE_HEADER_CONTENT_TYPE_KEY,
            AuthorizationConstants.REQUEST_HEADER_ACCEPT_VALUE);
        try {
            // 封装要传送的删除设备共享关系的报文 
            method.setRequestEntity(new StringRequestEntity(xmlData, fileType, unicodeType));
            
            // 执行请求
            client.executeMethod(method);
        }catch (Exception e){
           e.getStackTrace();
        }
        if (result401 == method.getStatusCode())
        {
            String authorizationStr = "";
            
            //获得响应中的名为"WWW-Authenticate"的头
            Header authenticateHeader = method.getResponseHeader(AuthorizationConstants.RESPONSE_HEADER_AUTHORIZATION_KEY_RESP_401);
            if (null != authenticateHeader)
            {
                authorizationStr = authenticateHeader.getValue();
            }
            StringBuffer sb = new StringBuffer(authorizationStr);
            
            // 先Authenticate的value做简单截取获得字符串数组
            int index = sb.indexOf(" ");
            if (-1 != index)
            {
                sb.delete(0, index + 1);
            }
            String[] authorizationParam = sb.toString().split(",");
            
            //解析响应中的名为"WWW-Authenticate"的头，填充到AuthenticateDigest类的对象，记录着各个字段,用于下次请求
            digestTmp = resolveAuthorization(digestTmp, authorizationParam);
            
            // 获取加密后用于鉴权的值
            digestTmp.setResponse(SHA256DigestUtil.generateResponse(digestTmp, null));
            
            //再次拼接Authorization头鉴权信息
            StringBuffer authorizationSec = getAuthorization();
            method.setRequestHeader(AuthorizationConstants.RESPONSE_HEADER_AUTHORIZATION_KEY,
                authorizationSec.toString());
            
            // 再次执行请求
            try {
                client.executeMethod(method);
            } catch (Exception e){
            	e.getStackTrace();
            }
        }
    }

    /**
     * 解析Authorization中的值
     * @param authorizationParam 认证参数数组，形如：<br/>
     *        [username="ivs@huawei.com", realm="huawei.com",
     *         nonce="NDY2NTUyNTc0OTM4NTEyODU2MDIxMTQyMTc2MjE1NjU=", 
     *         uri="/SCC/login.do", opaque="http", cnonce="00000000000000000000000000000000", 
     *         qop="auth", nc="00000001", response="fd8242ebbbca8cae276754ecd6dcc18b"]
     *         
     * @return HTTP摘要认证对象
     */
    private static AuthenticateDigest resolveAuthorization(AuthenticateDigest digest, String[] authorizationParam)
    {
        String tempStr = "";
        String key = "";
        String value = "";
        for (int i = 0; i < authorizationParam.length; i++)
        {
            tempStr = authorizationParam[i];
            
            // 如果"="不在字符串出现，则返回 -1
            int index = tempStr.indexOf("=");
            if (-1 == index)
            {
                continue;
            }
            
            // 去空格
            key = tempStr.substring(0, index).trim();
            
            // 去除字符串中存在的\"
            value = tempStr.substring(index + 1).replaceAll("\"", "");
            if (AuthorizationConstants.AUTHORIZATION_REALM_KEY.equals(key))
            {
                digest.setRealm(value);
                continue;
            }
            if (AuthorizationConstants.AUTHORIZATION_NONCE_KEY.equals(key))
            {
                digest.setNonce(value);
                continue;
            }
            if (AuthorizationConstants.AUTHORIZATION_USERNAME_KEY.equals(key))
            {
                digest.setUserName(value);
                continue;
            }
            if (AuthorizationConstants.AUTHORIZATION_CNONCE_KEY.equals(key))
            {
                digest.setCnonce(value);
                continue;
            }
            if (AuthorizationConstants.AUTHORIZATION_QOP_KEY.equals(key))
            {
                digest.setQop(value);
                continue;
            }
            if (AuthorizationConstants.AUTHORIZATION_RESPONSE_KEY.equals(key))
            {
                digest.setResponse(value);
                continue;
            }
            if (AuthorizationConstants.AUTHORIZATION_NC_KEY.equals(key))
            {
                digest.setNc(value);
                continue;
            }
            if (AuthorizationConstants.AUTHORIZATION_URI_KEY.equals(key))
            {
                digest.setUri(value);
                continue;
            }
            if (AuthorizationConstants.AUTHORIZATION_OPAQUE_KEY.equals(key))
            {
                digest.setOpaque(value);
                continue;
            }
        }
        return digest;
    }
    /**
     * 拼接鉴权头消息
     * @return StringBuffer 鉴权头消息
     */
    public static StringBuffer getAuthorization()
    {
        StringBuffer authorization = new StringBuffer(bufferLength);
        authorization.append("Digest realm=\"");
        authorization.append(AuthorizationConstants.AUTHORIZATION_REALM_VALUE);
        authorization.append("\",nonce=\"");
        authorization.append(digestTmp.getNonce());
        authorization.append("\",cnonce=\"");
        authorization.append(digestTmp.getCnonce());
        authorization.append("\",nc=\"");
        authorization.append(digestTmp.getNc());
        authorization.append("\",uri=\"");
        authorization.append(digestTmp.getUri());
        authorization.append("\",qop=\"");
        authorization.append(AuthorizationConstants.AUTHORIZATION_QOP_VALUE);
        authorization.append("\",algorithm=");
        authorization.append(AuthorizationConstants.AUTHORIZATION_ALGORITHM_VALUE);
        authorization.append(",opaque=\"");
        authorization.append(AuthorizationConstants.AUTHORIZATION_OPAQUE_VALUE);
        authorization.append("\",response=\"");
        authorization.append(digestTmp.getResponse());
        authorization.append(AuthorizationConstants.AUTHORIZATION_NONCE_VALUE_END);
        authorization.length();
        return authorization;
    }
    
	 /**
     * 后台登陆华为视频监控平台返回loginXML，解析xml并存入会话中
     * @param request
     * @param map
     */
    
	public static  Map<String, String> sendInitLoginData(Map<String, String>  map){
    	DefaultHttpClient httpclient = new DefaultHttpClient();
    	Map<String, String> loginMap = new HashMap<String, String>();
    	
    	  try {
    		  
	            KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());
	       		FileInputStream instream = 	new FileInputStream(new File(Thread.currentThread().getContextClassLoader().getResource("").getPath()+keystoreFile));
	            try {
				   //加载keyStore 
	                trustStore.load(instream, keyStorePassword.toCharArray());
	            } catch (Exception e) {
	            	logger.error("加载keyStore异常！");
	            	e.getStackTrace();
				}finally {
	                instream.close();
	            } 
				
				/**
				 * 手动更改为单向验证访问权限
				 */
				 X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier() {
					public boolean verify(String arg0, SSLSession arg1) { return true;   }  
					public void verify(String arg0, SSLSocket arg1) throws IOException {}  
					public void verify(String arg0, String[] arg1, String[] arg2) throws SSLException {}  
					public void verify(String arg0, X509Certificate arg1) throws SSLException {}  
				 };  

				// 穿件Socket工厂,将trustStore注入
	            SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore);
	            socketFactory.setHostnameVerifier(hostnameVerifier);
				//创建Scheme
	            Scheme sch = new Scheme(HTTPS, Integer.parseInt(map.get("sruServerPort").toString()), socketFactory); 
				//注册Scheme
	            httpclient.getConnectionManager().getSchemeRegistry().register(sch); 
	            
	            String xmlData = MsgTemplateXML.loginXMLToJava(map);
		   		 //接口回调消息
	            String nvsUrl = map.get("sruServer");//sruServer
	            HttpPost method = new HttpPost(nvsUrl);    
	            method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 0);
		        
		        //添加通用头域
	            method.addHeader(AuthorizationConstants.REQUEST_HEADER_REQ_URI_KEY, nvsUrl);
	            method.addHeader(AuthorizationConstants.REQUEST_HEADER_HTTP_VERSION_KEY,AuthorizationConstants.REQUEST_HEADER_HTTP_VERSION_VALUE);
	            method.addHeader(AuthorizationConstants.RESPONSE_HEADER_CONNECTION_KEY, connection);
		        
		        // 设置http请求消息头 DATE
		        Date currentDate = new Date();
		        String responseHeaderDateValue = new SimpleDateFormat("EEE,dd MMM yyyy HH:mm:ss", Locale.US).format(currentDate) + timeZoon;
		        method.addHeader(AuthorizationConstants.RESPONSE_HEADER_DATE_KEY, responseHeaderDateValue);
		        method.addHeader(AuthorizationConstants.REQUEST_HEADER_USER_AGENT_KEY,AuthorizationConstants.REQUEST_HEADER_USER_AGENT_VALUE);
			        
		        // 获取加密后用于鉴权的值
		        digestTmp.setResponse(SHA256DigestUtil.generateResponse(digestTmp, null));
		        
		        //各字段拼接生成请求的头类似Digest realm="huawei.com",qop="auth"
		        StringBuffer authorization = getAuthorization();
		        method.addHeader(AuthorizationConstants.RESPONSE_HEADER_AUTHORIZATION_KEY, authorization.toString());
		        
		        method.addHeader(AuthorizationConstants.REQUEST_HEADER_ACCEPT_KEY,AuthorizationConstants.REQUEST_HEADER_ACCEPT_VALUE);
		        method.addHeader(AuthorizationConstants.RESPONSE_HEADER_CONTENT_TYPE_KEY,AuthorizationConstants.REQUEST_HEADER_ACCEPT_VALUE);
		        // 设置http请求消息头 Host
		        //method.addHeader(AuthorizationConstants.REQUEST_HEADER_HOST_KEY, "10.5.61.43" + ":" + "8443");
		        method.addHeader(AuthorizationConstants.REQUEST_HEADER_HOST_KEY, map.get("sruServerIP") + urlConnectChar + map.get("sruServerPort"));
		      //  String authorizationStr = "";
		      
	            // 获取加密后用于鉴权的值
	            digestTmp.setResponse(SHA256DigestUtil.generateResponse(digestTmp, null));
	            
	            //再次拼接Authorization头鉴权信息
	            StringBuffer authorizationSec = getAuthorization();
	            method.addHeader(AuthorizationConstants.RESPONSE_HEADER_AUTHORIZATION_KEY,authorizationSec.toString());
	            method.setEntity(new StringEntity(xmlData, fileType, unicodeType));
	            HttpResponse res = httpclient.execute(method);
	            HttpEntity entity = res.getEntity();
	            if (entity != null) {
	              String logonXML = EntityUtils.toString(entity);
	              //销毁entity
				  EntityUtils.consume(entity); 
				  
				  boolean b = validate(logonXML);
				  if(b){
				  //解析returnXML
			 	  loginMap = getLoginData(logonXML);
				loginMap.put("orgCode", loginMap.get("orgCode"));
				loginMap.put("loginName", map.get("loginName"));
				loginMap.put("loginPassword", map.get("loginPassword"));
				loginMap.put("loginIp", map.get("loginIp"));
				loginMap.put("loginPort", map.get("loginPort"));
				loginMap.put("sruServerIP", map.get("sruServerIP"));
				loginMap.put("sruServerPort", map.get("sruServerPort"));
				loginMap.put("sruServer", map.get("sruServer"));
				loginMap.put("smuServer", map.get("smuServer"));
				loginMap.put("scuServer", map.get("scuServer"));
	            } else{
	            	logger.error("验证视频登陆信息失败！");
	            }
	            }
	        }catch (Exception e) {
	        	logger.error("后台登陆华为视频监控平台返回loginXML，解析xml并存入会话出错！");
				e.printStackTrace();
			} finally {
	            httpclient.getConnectionManager().shutdown();
	        }
			return loginMap;
    }
	
	private static boolean validate(String logonXML) throws Exception {
		boolean b=false;
			StringBuilder sXML = new StringBuilder();
			sXML.append(logonXML);
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			InputStream is = new ByteArrayInputStream(sXML.toString().getBytes("utf-8"));
			Document doc = dbf.newDocumentBuilder().parse(is);
			String resultCode = doc.getElementsByTagName("RESULT_CODE").item(0).getTextContent();
			if(resultCode.equals("0")){
				b=true;
			}else{
				throw new Exception("验证视频登陆信息失败！");
			}
		return b;
	}

	/**
	 * 获得登陆数据
	 * @param logonXML
	 * @return
	 */
	 public static Map<String,String> getLoginData(String logonXML){
			Map<String,String> map = new HashMap<String, String>();
			StringBuilder sXML = new StringBuilder();
			try{
			sXML.append(logonXML);
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			InputStream is = new ByteArrayInputStream(sXML.toString().getBytes("utf-8"));
			Document doc = dbf.newDocumentBuilder().parse(is);
			NodeList nl = doc.getElementsByTagName("ORG_CODE");//根据标签获得列表
	        Node org_Code=nl.item(0);//取列表中第一个对象
	        String orgCode=org_Code.getTextContent();//获取对象的值
	        NodeList n2 = doc.getElementsByTagName("LOGIN_ID");//根据标签获得列表
	        Node login_Id=n2.item(0);//取列表中第一个对象
	        String loginId=login_Id.getTextContent();//获取对象的值
	        map.put("orgCode", orgCode);
	        map.put("loginId", loginId);
	        logger.info("loginId:"+loginId+",orgCode:"+orgCode);
			}catch(Exception e){
				logger.error("获得登陆数据异常！");
				e.printStackTrace();
			}
	        return map;
	        
		}
}
